use std::fs::{OpenOptions,File,create_dir};
use std::path::PathBuf;
use std::io::{Write, Read};
use log::{LevelFilter, Record, Metadata, SetLoggerError};
use parking_lot::Mutex;
use std::collections::HashMap;


lazy_static! {
    pub static ref MYLOGGER:MyLogger = MyLogger::from_path("./mylog").unwrap();
}

pub struct MyLogger {
    path:String,
    log_map:Mutex<HashMap<String,File>>,
}

impl MyLogger {
    pub fn from_path(path:&str) -> std::io::Result<Self> {
        let _ = create_dir(path);
        Ok(Self {
            path:path.to_string(),
            log_map:Mutex::new(HashMap::new()),
        })
    }
}

impl log::Log for MyLogger {
    fn enabled(&self, _: &Metadata) -> bool {
        true
    }

    fn log(&self, record: &Record) {
        let path = record.target().to_string();
        let mut log_map = self.log_map.lock();
        let mut file = match log_map.get(&path) {
            Some(file) => {
                file
            },
            None => {
                let dir:PathBuf = PathBuf::from(&self.path);
                let target_path = PathBuf::from(&path);
                let create_dir = dir.join(target_path.parent().unwrap());
                if let Err(e) = std::fs::create_dir_all(create_dir) {
                    panic!("{e}");
                }
                match OpenOptions::new().create(true).append(true).open(dir.join(format!("{}.log",path))) {
                    Ok(file) => {
                        log_map.insert(path.clone(), file);
                        &log_map[&path]
                    }
                    Err(e) => { panic!("{}",e); }
                }
            }
        };
        match file.write_fmt(format_args!("{}:{}", record.level(),record.args())) {
            Ok(()) => {},
            Err(e) => { panic!("{}",e); },
        }
    }
    fn flush(&self) {}
}

pub fn mylog_init() -> Result<(),SetLoggerError> {
    log::set_logger(&*MYLOGGER)?;
    Ok(())
}


pub fn config_log_level(level:&str) -> Option<LevelFilter> {
    let level = match level {
        "error" => LevelFilter::Error,
        "warn" => LevelFilter::Warn,
        "info" => LevelFilter::Info,
        "debug" => LevelFilter::Debug,
        "trace" => LevelFilter::Trace,
        "default" => LevelFilter::Debug,
        _ => return None,
    };
    log::set_max_level(level);
    Some(level)
}

pub fn u8_to_file(path:&str,bytes:&[u8]) {
    let mut file = OpenOptions::new().create(true).write(true).open(path).expect("cao");
    file.write(bytes).expect("write error");
}

pub fn file_to_u8(path:&str) -> Vec<u8> {
    let mut file = File::open(path).expect("open file to u8");
    let mut result = Vec::new();
    file.read_to_end(&mut result).expect("read error");
    result
}

fn _delta_check() {
    let backup_src = file_to_u8("backup_matching_src");
    let backup_target = file_to_u8("backup_matching_target");
    assert!(_xdelta_check(&[1, 2, 3, 4, 5, 6, 7], &[1, 2, 4, 4, 7, 6, 7]));
    assert!(_xdelta_check(&backup_target, &backup_src));
}

fn _xdelta_check(a:&[u8],b:&[u8]) -> bool {
    let c = xdelta3::encode(a, b,a.len()).unwrap();
    println!("a({}) - b({}) = c({})",a.len(),b.len(),c.len());
    let a_ = xdelta3::decode(&c, b, a.len());
    a_.map_or(false, |a_|a_==a)
}